package cn.caipengbo.db;

import cn.caipengbo.domain.Classes;
import cn.caipengbo.domain.Student;
import cn.caipengbo.utils.HibernateUtils;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import java.util.HashSet;
import java.util.Set;


/**
 * 一对多的单项
 * @author zd
 *				一般操作
				1、保存班级
				2、保存学生
				3、保存班级，保存学生

				级联操作
				4、保存班级级联保存学生
				5、保存班级级联更新学生
				6、更新班级级联保存学生
				7、更新班级级联更新学生
				8、删除班级级联删除学生
				9、在班级有级联save-update的情况下，从关联得到学生，并且删除学生？

				关系操作
				8、已经存在一个班级，新建一个学生，把该学生加入到该班级(建立关系操作)
				9、已经存在一个学生，新建一个班级，把该学生加入到该班级(建立关系操作)
				10、已经存在一个学生，已经存在一个班级，把该学生加入到该班级
				11、已经存在一个学生，把一个学生从一个班级转移到另外一个班级
				
				在一的一方维护关系的时候，总会发出维护关系的update语句，该update语句就是更新外键

				级联和关系的混合：
				12、在删除班级的时候，解除班级和学生之间的关系
 */
public class OneToManySingleTest extends HibernateUtils {
	@Test
	public void testSaveClasses(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		Classes classes = new Classes();
		classes.setName("黑马JavaEE+hadoop的12期");
		classes.setDescription("牛");
		session.save(classes);
		transaction.commit();
	}
	
	@Test
	public void testSaveStudent(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		Student student = new Student();
		student.setName("班长");
		student.setDescription("带头大哥");
		session.save(student);
		transaction.commit();
	}
	
	@Test
	public void testSaveClassesAndSaveStudent(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		Classes classes = new Classes();
		classes.setName("黑马视频班");
		classes.setDescription("野牛");
		
		Student student = new Student();
		student.setName("班秘");
		student.setDescription("凤姐");
		
		session.save(classes);
		session.save(student);
		transaction.commit();
	}
	
	/**
	 * 在保存班级的时候，级联保存学生
	 */
	@Test
	public void testSaveClasses_Cascade_Save_Student(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		Classes classes = new Classes();
		classes.setName("黑马视频班");
		classes.setDescription("野牛");
		
		Student student = new Student();
		student.setName("班秘");
		student.setDescription("凤姐");
		
		//建立classes与student之间的关联
		Set<Student> students = new HashSet<Student>();
		students.add(student);
		classes.setStudents(students);
		
		session.save(classes); //显示保存班级，把保存student称为隐式保存
		transaction.commit();
	}
	
	/**
	 * 在更新班级的时候，级联更新学生
	 * sessin.flush的时候
	 *    1、检查一级缓存中所有的持久化状态的对象
	 *        判断发出insert语句或者update语句
	 *    2、检查所有的持久化对象的关联对象
	 *         如果关联对象是由临时状态转化过来的，则对关联对象发出insert语句
	 *         如果关联对象是从数据库中提取出来的，则对照副本，决定是否发出update语句
	 */
	@Test
	public void testUpdateClasses_Cascade_Update_Student(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		Classes classes = (Classes)session.get(Classes.class, 1L);//持久化类
		Set<Student> students = classes.getStudents();//也是持久化类
		for (Student student : students) {//把每一个student对象(关联对象)也放入到了一级缓存中
			student.setDescription("111");
		}
		transaction.commit();
	}
	
	/**
	 * 在更新班级的时候，添加学生
	 */
	@Test
	public void testUpdateClasses_Cascade_Save_Student(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		/*
		 * 给cid为1的班级添加一个学生
		 */
		Classes classes = (Classes)session.get(Classes.class, 1L);
		
		Student student = new Student();
		student.setName("新名字");
		student.setDescription("添加的描述");
		
		/**
		 * 建立班级与学生的关联（在班级集合内添加学生）
		 */
		classes.getStudents().add(student);
		
		transaction.commit();
	}
	
	/**
	 * 在更新班级的时候，级联保存学生，并且维护关系
	 * 	Hibernate: 
		    select
		        classes0_.cid as cid0_0_,classes0_.description as descript2_0_0_,classes0_.name as name0_0_
		    from
		        Classes classes0_ 
		    where
		        classes0_.cid=?
		Hibernate: 
		    select
		        students0_.cid as cid0_1_,
		        students0_.sid as sid1_,
		        students0_.sid as sid1_0_,
		        students0_.description as descript2_1_0_,
		        students0_.name as name1_0_ 
		    from
		        Student students0_ 
		    where
		        students0_.cid=?
		Hibernate: 
		    select
		        max(sid) 
		    from
		        Student
		Hibernate: 
		           因为在Classes.hbm.xml文件中设置了级联
		         <set name="students" cascade="save-update">
		    insert 
		    into
		        Student
		        (description, name, sid) 
		    values
		        (?, ?, ?)
		Hibernate: 
		           因为在Classes.hbm.xml文件中，inverse没有写，默认classes维护classes与student之间的关系
		           所以发出了更新关系的update语句
		    update
		        Student 
		    set
		        cid=? 
		    where
		        sid=?
	 */
	@Test
	public void testUpdateClasses_Cascade_Save_Student_Inverse(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		/*
		 * 给cid为1的班级添加一个学生
		 */
		Classes classes = (Classes)session.get(Classes.class, 1L);
		
		Student student = new Student();
		student.setName("新3");
		student.setDescription("不维护关系测试");
		
		/**
		 * 建立班级与学生的关联
		 */
		classes.getStudents().add(student);
		
		transaction.commit();
	}
	
	/**
	 * 班级转移
	 * 已经存在一个班级cid为1（有一个学生），已经存在另外一个班级cid为2，学生从cid为1的班级转到cid为2的班级
	 */
	@Test
	public void testTransform(){
		/**
		 * 1、把cid为1，2和sid为1的对象提取出来
		 */
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		
		//Classes classes1 = (Classes)session.get(Classes.class, 1L);
		Classes classes2 = (Classes)session.get(Classes.class, 2L);//发出查询的sql语句
		Student student = (Student)session.get(Student.class, 1L);//发出查询的sql语句
		
		//解除 classes1与student之间的关系
		//classes1.getStudents().remove(student);
		//建立classes2与student之间的关系
		classes2.getStudents().add(student); // Hibernate会自动执行以上注释的操作
		transaction.commit();
	}
	
	/**
	 * 解除该班级和该班级中的所有的学生之间的关系
	 */
	@Test
	public void testRealseR(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		/**
		 * 解除cid为1的班级和所有的学生之间的关系
		 */
		Classes classes = (Classes)session.get(Classes.class,1L);
		// Hibernate会自动将该班级学生的外键置为Null
		classes.setStudents(null);
		transaction.commit();
	}
	
	/**
	 * 解除该班级和所有的学生之间的关系，再建立该班级和一些学生之间的关系
	 */
	@Test
	public void testRealseAllR_BuildSomeR(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		//解除cid为2的班级和所有的学生之间的关系
		Classes classes = (Classes)session.get(Classes.class, 2L);
		classes.setStudents(null);
		
		Student student1 = new Student();
		student1.setName("王二麻子");
		
		Student student2 = new Student();
		student2.setName("隔壁老李");
		Set<Student> students = new HashSet<Student>();
		students.add(student2);
		students.add(student1);
		classes.setStudents(students);
		
		transaction.commit();
	}
	
	/**
	 * 删除一个班级
	 *   在删除班级之前，解除班级和学生之间的关系
	 *   Hibernate: 
		    select
		        classes0_.cid as cid0_0_,
		        classes0_.description as descript2_0_0_,
		        classes0_.name as name0_0_ 
		    from
		        Classes classes0_ 
		    where
		        classes0_.cid=?
		Hibernate: 
		           因为classes负责维护关系，所以该语句就是解除关系的sql语句
		    update
		        Student 
		    set
		        cid=null 
		    where
		        cid=?
		Hibernate: 
		    delete 
		    from
		        Classes 
		    where
		        cid=?

	 */
	@Test
	public void testDeleteClasses_1(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		Classes classes = (Classes)session.get(Classes.class, 1L);
		session.delete(classes); // Hibernate自动解除与该班级有关的关系
		transaction.commit();
	}
	
	/**
	 * 在 Classes.hbm.xml文件中
	 *     <set name="students" cascade="all">
	 *     在删除班级的时候，级联删除学生
	 */
	@Test
	public void testDeleteClasses_2(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		Classes classes = (Classes)session.get(Classes.class, 1L);
		session.delete(classes); //
		transaction.commit();
	}
	
	/**
	 * 删除一个学生，但是该学生必须从班级中提取出来
	 *   因为在Classes.hbm.xml文件中
	 *   	<set name="students" cascade="save-update">
	 *      classes针对student是关联的
	 *      而程序中的student对象是从classes中提取出来的关联对象，所以不能删除
	 */
	@Test
	public void testError(){
		Session session = sessionFactory.getCurrentSession();
		Transaction transaction = session.beginTransaction();
		Classes classes = (Classes)session.get(Classes.class, 1L);
		Set<Student> students = classes.getStudents();
		//classes.setStudents(null);//解除关系
		for (Student student : students) {
			session.delete(student);
		}
		transaction.commit();
	}
}
